//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using Microsoft.Research.DataLayer;
using System.Threading;
using Microsoft.Research.ScientificWorkflow.TridentUtilities;

namespace Microsoft.Research.eResearch.WFServiceLibrary
{
    /// <summary>
    /// Class for holding the first created connection between threads.
    /// </summary>
    public class ThreadBase
    {
        /// <summary>
        /// Gets or sets the static registry handle.
        /// </summary>
        /// <value>The static registry handle.</value>
        public static Connection StaticRegistryHandle { get; set; }
        /// <summary>
        /// Initial number of concurrent jobs
        /// </summary>
        private const int numberOfConCurrentJobs  = 1;

        static Mutex myMutex = new Mutex(false);

        /// <summary>
        /// Gets the scheduled jobs.
        /// </summary>
        /// <param name="currentNode">The current node.</param>
        /// <param name="RegistryHandle">The registry handle.</param>
        /// <returns></returns>
        internal static Collection<JobSchedule> GetPendingSchedules(Machine currentNode, Connection RegistryHandle)
        {
            myMutex.WaitOne();
            try
            {
                Collection<JobSchedule> schedules = new Collection<JobSchedule>();

                foreach (JobSchedule schedule in JobSchedule.CommonSearches.GetPendingSchedules(RegistryHandle))
                {
                    if (schedule.Template.Machines[0].Name == currentNode.Name)
                    {                
                       schedules.Add(schedule);
                    }
                }

                schedules.OrderBy(schedule => schedule.Created.When).ToList();

                return schedules;
            }            
            catch (Exception exp)
            {
                TridentErrorHandler.ThrowTridentCustomException(
                    TridentErrorConstants.ErrorNumber1200000001,
                    ManagementStudioResourceManager.GetString("GetScheduledJobsError") + exp.Message);
                return null;
            }
            finally
            {
                myMutex.ReleaseMutex();
            }
        }

        /// <summary>
        /// Creates the machine.
        /// </summary>
        /// <param name="hostName">Name of the host.</param>
        /// <param name="RegistryHandle">The registry handle.</param>
        /// <returns></returns>
        internal static Machine CreateMachine(string hostName, Connection RegistryHandle)
        {
            myMutex.WaitOne();
            try
            {
                Machine machine = Machine.Create(hostName,
                    MachineType.Desktop,
                    RegistryHandle);
                HealthCheck.Create(machine, RegistryHandle);
                machine.MaxConcurrentJobs = numberOfConCurrentJobs;
                return machine;
            }
            catch (Exception exp)
            {
                TridentErrorHandler.ThrowTridentCustomException(
                    TridentErrorConstants.ErrorNumber1200000002, 
                    ManagementStudioResourceManager.GetString("CreateMachineError") + exp.Message);
                return null;
            }
            finally
            {
                myMutex.ReleaseMutex();
            }
        }

        /// <summary>
        /// Loads the machine.
        /// </summary>
        /// <param name="hostName">Name of the host.</param>
        /// <param name="registryHandle">The registry handle.</param>
        /// <param name="isInteractive"></param>
        /// <returns></returns>
        internal static Machine LoadMachine(string hostName, Connection registryHandle, bool isInteractive)
        {
            Machine machine = null;
            myMutex.WaitOne();
            try
            {
                Collection<Machine> machines = Machine.CommonSearches.GetMachineByName(registryHandle, isInteractive, hostName);
                if (machines != null && machines.Count > 0)
                {
                    machine = machines[0];
                }
            }
            finally
            {
                myMutex.ReleaseMutex();
            }
            return machine;
        }

        /// <summary>
        /// Deletes the selected schedule.
        /// </summary>
        /// <param name="schedule">The schedule.</param>
        internal static void DeleteSelectedSchedule(JobSchedule schedule)
        {
            myMutex.WaitOne();
            try
            {
                // Delete the job.
                schedule.Template.Delete();

                // Now delete the Schedule.
                schedule.Delete();
            }
            catch (Exception exp)
            {
                TridentErrorHandler.ThrowTridentCustomException(
                    TridentErrorConstants.ErrorNumber1200000003, 
                    ManagementStudioResourceManager.GetString("DeleteSelectedScheduleError") + exp.Message);
            }
            finally
            {
                myMutex.ReleaseMutex();
            }
        }

        /// <summary>
        /// Returns a list of all the jobs on the server with the
        /// status as Waiting.
        /// </summary>
        /// <param name="server">Machine on which the jobs would execute.</param>
        /// <returns>List of jobs to be executed.</returns>
        internal static List<Job> GetJobsToRun(Machine server)
        {
            myMutex.WaitOne();
            List<Job> wfJobs = null;
            try
            {
                wfJobs = server.GetJobsToRun();                
                // Order by the latest created.
                return wfJobs.OrderBy(job => job.Created.When).ToList();                
            } 
            catch (RecordNotFoundException)
            {               
            }           
            finally
            {
                myMutex.ReleaseMutex();
            }
            return wfJobs;
        }


        /// <summary>
        /// Returns a list of all the jobs on the server with the
        /// status as Waiting.
        /// </summary>
        /// <param name="server">Machine on which the jobs would execute.</param>
        /// <param name="conn">registry connection to get the jobs.</param>
        /// <returns>List of jobs to be executed.</returns>
        internal static List<Job> GetJobsToVerify(Machine server, Connection conn)
        {
            //Using the same mutex becuase we are refresing the server object 
            myMutex.WaitOne();
            List<Job> wfJobs = null;
            
            try
            {
                wfJobs = server.GetJobsToVerify();                   
                return wfJobs;
            }
            catch (RecordNotFoundException)
            {                
            }  
            finally
            {
                myMutex.ReleaseMutex();
            }
            return wfJobs;
        }

        /// <summary>
        /// Updates the execution service status.
        /// </summary>
        /// <param name="node">The node.</param>
        public static void UpdateExecutionServiceStatus(Machine node)
        {
            string errorMessage = ManagementStudioResourceManager.GetString("ServiceStatus");

            myMutex.WaitOne();
            try
            {
                node.Status = MachineStatus.Running;
                node.ExecutorStatusMessage = errorMessage + node.Status;
                node.LastPollTime = DateTime.UtcNow;
            }
            catch (Exception exp)
            {
                TridentErrorHandler.ThrowTridentCustomException(
                    TridentErrorConstants.ErrorNumber1200000005, 
                    ManagementStudioResourceManager.GetString("UpdateExecutionServiceStatusError") + exp.Message);
            }
            finally
            {
                myMutex.ReleaseMutex();
            }
        }

        /// <summary>
        /// Updates the scheduler service status.
        /// </summary>
        /// <param name="node">The node.</param>
        /// <param name="schedulerStatus">The scheduler status.</param>
        public static void UpdateSchedulerServiceStatus(Machine node, SchedulerStatus schedulerStatus)
        {
            string statusMessage = ManagementStudioResourceManager.GetString("ServiceStatus");

            myMutex.WaitOne();
            try
            {
                node.SchedulerStatusMessage = statusMessage + schedulerStatus;
                node.SchedulerStatus = schedulerStatus;
            }
            catch (Exception exp)
            {
                TridentErrorHandler.ThrowTridentCustomException(
                    TridentErrorConstants.ErrorNumber1200000006, 
                    ManagementStudioResourceManager.GetString("UpdateSchedulerServiceStatusError") + exp.Message);
            }
            finally
            {
                myMutex.ReleaseMutex();
            }
        }
    }
}
